package com.iteaj.iot.taos;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.SqlParameterValue;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class DefaultTaosSqlManager implements TaosSqlManager, BeanFactoryAware, InitializingBean {

    private BeanFactory beanFactory;

    private ThreadPoolTaskScheduler taskScheduler;

    private Logger logger = LoggerFactory.getLogger(getClass());
    private ExpressionParser parser = new SpelExpressionParser();
    private Map<Class<?>, SqlContext> sqlContextMap = new ConcurrentHashMap<>(16);
    private Map<Class<?>, EntityMetas> entityMetasMap = new ConcurrentHashMap<>(16);

    @Override
    public String getTableName(Object entity) {
        EntityMetas entityMeta = this.getEntityMeta(entity.getClass());

        // 获取数据表名
        String tableName = entityMeta.getsTable().table();

        // 解析数据表表名
        return parser.parseExpression(tableName).getValue(entity, String.class);
    }

    @Override
    public EntityMetas getEntityMeta(Class entityClass) {
        EntityMetas entityMetas = entityMetasMap.get(entityClass);
        if(entityMetas == null) {
            synchronized (this) {
                entityMetas = new EntityMetas(entityClass).resolve(this.beanFactory);
                if(entityClass == null) {
                    throw new TaosException("["+TaosHandle.class.getSimpleName()+"]对象的返回实体对象必须使用"+STable.class.getSimpleName()+"注解");
                }

                entityMetasMap.put(entityClass, entityMetas);
                sqlContextMap.put(entityClass, new SqlContext(entityMetas));
            }
        }

        return entityMetas;
    }

    @Override
    public SqlContext getSqlContext(Class entityClass) {
        SqlContext sqlContext = sqlContextMap.get(entityClass);
        if(sqlContext == null) {
            this.getEntityMeta(entityClass);
        }

        return sqlContextMap.get(entityClass);
    }

    @Override
    public SqlContext addEntity(Object entity) {
        SqlContext sqlContext = this.getSqlContext(entity.getClass());

        // 获取数据表名
        String tableName = sqlContext.getMeta().getsTable().table();

        // 解析数据表表名
        tableName = parser.parseExpression(tableName).getValue(entity, String.class);

        // 解析参数
        List<SqlParameterValue> params = sqlContext.getMeta().getParams(entity);
        return sqlContext.addEntitySql(tableName, params);
    }

    @Override
    public SqlContext addEntity(Class entityClass, List<Object> entities) {
        SqlContext sqlContext = this.getSqlContext(entityClass);
        // 获取数据表名
        String tableName = sqlContext.getMeta().getsTable().table();

        entities.forEach(entity -> {
            String value = parser.parseExpression(tableName).getValue(entity, String.class);

            // 解析普通字段参数
            List<SqlParameterValue> params = sqlContext.getMeta().getParams(entity);

            sqlContext.addEntitySql(new EntitySql(value, params));
        });

        return sqlContext;
    }

    @Override
    public void execute(Object entity, TaosHandle handle) {
        // 定时批量写入
        if(handle.maxOfPeer() > 1) {
            SqlContext sqlContext = this.addEntity(entity);

            // 初始化SqlContext对象
            if(sqlContext.getTaosHandle() == null) {
                synchronized (sqlContext) {
                    if(sqlContext.getTaosHandle() == null) {
                        // 加入任务调度
                        taskScheduler.scheduleWithFixedDelay(sqlContext, Instant.now(), Duration.ofSeconds(handle.period()));
                    }

                    sqlContext.setTaosHandle(handle);
                }

                sqlContext.setJdbcTemplate(beanFactory.getBean(handle.jdbcTemplate(), JdbcTemplate.class));
            }
        } else { // 实时写入
            SqlContext sqlContext = this.getSqlContext(entity.getClass());
            JdbcTemplate jdbcTemplate = sqlContext.getJdbcTemplate();

            if(jdbcTemplate == null) {
                jdbcTemplate = beanFactory.getBean(handle.jdbcTemplate(), JdbcTemplate.class);
                sqlContext.setTaosHandle(handle);
                sqlContext.setJdbcTemplate(jdbcTemplate);
            }

            String tableName = this.getTableName(entity);
            sqlContext.update(tableName, entity);
        }
    }

    public Map<Class<?>, EntityMetas> getEntityMetasMap() {
        return entityMetasMap;
    }

    public void setEntityMetasMap(Map<Class<?>, EntityMetas> entityMetasMap) {
        this.entityMetasMap = entityMetasMap;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        this.taskScheduler = (ThreadPoolTaskScheduler) this.beanFactory.getBean(TaskScheduler.class);
    }
}
